﻿using Caltech.Extensions;
using Caltech.Models;
using Caltech.Service.Common;
using CaltechSoft.Service.CommandService;
using CaltechSoft.Service.CommandService.WorkOrder;
using CaltechSoft.Service.EntityFW.Entity;
using CaltechSoft.Service.EntityFW.Repository;
using CaltechSoft.Service.Infrastructure.Utility;
using CaltechSoft.Service.ReportService;
using CaltechSoft.Service.ReportService.WorkOrder;
using CaltechSoft.Service.Repositories;
using CaltechSoft.Service.SearchCriteria;
using LinqKit;
using Microsoft.AspNet.Identity;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;

namespace Caltech.Controllers
{
    [HasAccess(Areas.Report)]
    public class ReportsController : ApiController
    {
        private readonly IGenericRepository<Po> m_PoRespository;
        private readonly IGenericRepository<PoItem> m_PoItemRepository;
        private readonly IGenericRepository<WorkOrder> m_WorkOrderRepository;
        private readonly IGenericRepository<Quote> m_QuoteRespository;
        private readonly IGenericRepository<Certificate> m_CertificateRepository;
        private readonly IGenericRepository<CustomerEquipment> m_EquipmentRespository;
        private readonly IGenericRepository<Customer> m_CustomerRespository;
        private readonly IGenericRepository<WorkOrderDetails> m_WorkOrderDetailsRespository;
        private readonly IGenericRepository<CertificateStandard> m_CertificateStandardRespository;
        private readonly IGenericRepository<AE> m_AERespository;
        private readonly IRepository<ReportItem> m_ReportItemRespository;
        private UserManager<User> m_UserRepository { get; set; }

        public ReportsController(IGenericRepository<Po> poRepository, IGenericRepository<Quote> quoteRespository,
            IGenericRepository<CustomerEquipment> equipmentRespository, IGenericRepository<Customer> customerRespository,
             IGenericRepository<WorkOrder> workOrderRepository, IGenericRepository<Certificate> certificateRepository,
            IGenericRepository<WorkOrderDetails> workOrderDetailsRespository, IGenericRepository<CertificateStandard> certificateStandardRespository,
            IGenericRepository<PoItem> poItemRepository, IGenericRepository<AE> aeRepository, IRepository<ReportItem> reportItemRespository
            )
        {
            Guard.ArgumentIsNotNull(poRepository, "Po Repository is not null");
            Guard.ArgumentIsNotNull(workOrderRepository, "IWorkOderRepository, is not null");
            Guard.ArgumentIsNotNull(quoteRespository, "Quote Repository is not null");
            Guard.ArgumentIsNotNull(certificateRepository, "Certificate Repository is not null");
            Guard.ArgumentIsNotNull(equipmentRespository, "Equipment Repository is not null");
            Guard.ArgumentIsNotNull(customerRespository, "Customer Repository is not null");
            Guard.ArgumentIsNotNull(poItemRepository, "PO Item is not null");
            Guard.ArgumentIsNotNull(aeRepository, "AE is not null");
            Guard.ArgumentIsNotNull(reportItemRespository, "report is not null");


            m_PoRespository = poRepository;
            m_WorkOrderRepository = workOrderRepository;
            m_QuoteRespository = quoteRespository;
            m_CertificateRepository = certificateRepository;
            m_EquipmentRespository = equipmentRespository;
            m_CustomerRespository = customerRespository;
            m_WorkOrderDetailsRespository = workOrderDetailsRespository;
            m_CertificateStandardRespository = certificateStandardRespository;
            m_PoItemRepository = poItemRepository;
            m_AERespository = aeRepository;
            m_ReportItemRespository = reportItemRespository;
        }

        [Route("api/reports/yearlyreports")]
        public IHttpActionResult GetYearlyReport(int fromYear = 0, int toYear = 0)
        {
            var reports = new List<ReportModel>();

            try
            {
                for (int year = fromYear; year <= toYear; year++)
                {
                    var reportInYear = GetReportInYear(year);
                    if (reportInYear.Count > 0)
                    {
                        reports.Add(reportInYear[12]);
                    }
                    else
                    {
                        reportInYear.Add(new ReportModel());
                    }
                }
            }
            catch (Exception)
            {
                return Ok();
            }

            return Ok(reports);
        }

        [Route("api/reports/quarterlyReport")]
        public IHttpActionResult GetQuarterlyReport(int year = 0)
        {
            List<ReportModel> reportInYear = GetReportInYear(year);
            if (reportInYear.Count == 0)
            {
                return Ok(new List<ReportModel>());
            }

            var result = new List<ReportModel>();

            for (int i = 0; i < 12; i += 3)
            {
                result.Add(new ReportModel()
                {
                    Year = year,
                    NoOfQuote = reportInYear[i].NoOfQuote + reportInYear[i + 1].NoOfQuote + reportInYear[i + 2].NoOfQuote,
                    NoOfPO = reportInYear[i].NoOfPO + reportInYear[i + 1].NoOfPO + reportInYear[i + 2].NoOfPO,
                    NoOfNewCustomer = reportInYear[i].NoOfNewCustomer + reportInYear[i + 1].NoOfNewCustomer + reportInYear[i + 2].NoOfNewCustomer,
                    NoOfWO = reportInYear[i].NoOfWO + reportInYear[i + 1].NoOfWO + reportInYear[i + 2].NoOfWO,
                    TotalPOValue = reportInYear[i].TotalPOValue + reportInYear[i + 1].TotalPOValue + reportInYear[i + 2].TotalPOValue,
                    NoOfEquipmentCalibrated = reportInYear[i].NoOfEquipmentCalibrated + reportInYear[i + 1].NoOfEquipmentCalibrated + reportInYear[i + 2].NoOfEquipmentCalibrated,
                    NoOfStandardsDues = reportInYear[i].NoOfStandardsDues + reportInYear[i + 1].NoOfStandardsDues + reportInYear[i + 2].NoOfStandardsDues,
                    NoOfEquiptCalibratedByVendors = reportInYear[i].NoOfEquiptCalibratedByVendors + reportInYear[i + 1].NoOfEquiptCalibratedByVendors + reportInYear[i + 2].NoOfEquiptCalibratedByVendors,
                    NoOfCert = reportInYear[i].NoOfCert + reportInYear[i + 1].NoOfCert + reportInYear[i + 2].NoOfCert,
                    TotalAEExpenses = reportInYear[i].TotalAEExpenses + reportInYear[i + 1].TotalAEExpenses + reportInYear[i + 2].TotalAEExpenses,
                    TotalInvoiceValue = reportInYear[i].TotalInvoiceValue + reportInYear[i + 1].TotalInvoiceValue + reportInYear[i + 2].TotalInvoiceValue,
                    OperationExpenses = reportInYear[i].OperationExpenses + reportInYear[i + 1].OperationExpenses + reportInYear[i + 2].OperationExpenses,
                    Other1 = reportInYear[i].Other1 + reportInYear[i + 1].Other1 + reportInYear[i + 2].Other1,
                    Other2 = reportInYear[i].Other2 + reportInYear[i + 1].Other2 + reportInYear[i + 2].Other2,
                    Other3 = reportInYear[i].Other3 + reportInYear[i + 1].Other3 + reportInYear[i + 2].Other3,
                    Other4 = reportInYear[i].Other4 + reportInYear[i + 1].Other4 + reportInYear[i + 2].Other4,
                    Other5 = reportInYear[i].Other5 + reportInYear[i + 1].Other5 + reportInYear[i + 2].Other5,
                    Other6 = reportInYear[i].Other6 + reportInYear[i + 1].Other6 + reportInYear[i + 2].Other6,
                    Other7 = reportInYear[i].Other7 + reportInYear[i + 1].Other7 + reportInYear[i + 2].Other7,
                    Other8 = reportInYear[i].Other8 + reportInYear[i + 1].Other8 + reportInYear[i + 2].Other8,
                    Other9 = reportInYear[i].Other9 + reportInYear[i + 1].Other9 + reportInYear[i + 2].Other9,
                    Other10 = reportInYear[i].Other10 + reportInYear[i + 1].Other10 + reportInYear[i + 2].Other10,
                    Other11 = reportInYear[i].Other11 + reportInYear[i + 1].Other11 + reportInYear[i + 2].Other11,
                    Other12 = reportInYear[i].Other12 + reportInYear[i + 1].Other12 + reportInYear[i + 2].Other12,
                    Other13 = reportInYear[i].Other13 + reportInYear[i + 1].Other13 + reportInYear[i + 2].Other13,
                    Tax = reportInYear[i].Tax + reportInYear[i + 1].Tax + reportInYear[i + 2].Tax

                });
            }
            result.Add(reportInYear.Last());
            return Ok(result);
        }

        [Route("api/reports/monthlyreports")]
        public IHttpActionResult GetMonthlyReport(int year = 0)
        {
            var reports = new List<ReportModel>();
            reports = GetReportInYear(year);

            return Ok(reports);
        }


        public IHttpActionResult Post(ReportAdd model)
        {
            if (model != null)
            {
                var existedReport = m_ReportItemRespository.DbSet().AsExpandable().Where(x => x.Year == model.Year).FirstOrDefault();
                if (existedReport != null)
                {
                    m_ReportItemRespository.Remove(existedReport.Id);
                }
                var entity = new ReportItem()
                {
                    CreatedBy = User.Identity.GetUserId(),
                    CreatedDate = DateTime.Now,
                    Data = model.Data.ToString(),
                    Year = model.Year
                };

                m_ReportItemRespository.Add(entity);
            }
            return Ok();
        }

        private List<ReportModel> GetReportInYear(int year)
        {
            var reports = new List<ReportModel>();
            var firstDate = new DateTime(year, 1, 1, 0, 0, 0);
            var endDate = new DateTime(year, 12, 31, 23, 59, 59);

            Expression<Func<Quote, bool>> filterQuote =
                qo => qo.QuoteDate.HasValue
                          ? (qo.QuoteDate.Value >= firstDate &&
                             qo.QuoteDate.Value <= endDate) : false;

            Expression<Func<Po, bool>> filterPO =
                qo => qo.PODate.HasValue
                          ? (qo.PODate.Value >= firstDate &&
                             qo.PODate.Value <= endDate) : false;

            Expression<Func<WorkOrder, bool>> filterWO =
                wo => wo.CalibrationDate >= firstDate &&
                             wo.CalibrationDate <= endDate;

            Expression<Func<Certificate, bool>> filterCert =
               cert => cert.CalibrationDate.HasValue
                         ? (cert.CalibrationDate.Value >= firstDate &&
                            cert.CalibrationDate.Value <= endDate) : false;

            Func<Customer, bool> filterCustomer =
                 cus => cus.CreatedDate.HasValue
                           ? (cus.CreatedDate.Value >= firstDate &&
                              cus.CreatedDate.Value <= endDate) : false;

            Expression<Func<CertificateStandard, bool>> filterStandard =
                  sta => sta.CreatedDate.HasValue
                            ? (sta.CreatedDate.Value >= firstDate &&
                               sta.CreatedDate.Value <= endDate) : false;

            Expression<Func<AE, bool>> filterAE =
                  ae => ae.AEDate >= firstDate &&
                               ae.AEDate <= endDate;

            Expression<Func<Po, bool>> filterInvoice =
                  po => po.Status == (int)ProcessStatusPO.Invoice && po.InvoiceDate.HasValue ? (po.InvoiceDate >= firstDate &&
                               po.InvoiceDate <= endDate) : false;

            IList<Quote> quotesInYear = m_QuoteRespository.Get(filterQuote).ToList();

            IList<Po> poInYear = m_PoRespository.Get(filterPO).ToList();

            IList<Po> invoiceInYear = m_PoRespository.Get(filterInvoice).ToList();

            IList<Customer> allCustomers = m_CustomerRespository.Get().ToList();

            IList<Customer> customerInYear = allCustomers.Where(filterCustomer).ToList();

            IList<WorkOrder> woInYear = m_WorkOrderRepository.Get(filterWO).ToList();

            woInYear = (from wo in woInYear
                        join cus in allCustomers on wo.CompanyId equals cus.Id
                        where !string.IsNullOrEmpty(wo.UpdatedBy)
                        select wo).ToList();

            IList<Certificate> certInYear = m_CertificateRepository.Get(filterCert).ToList();

            List<WorkOrderDetails> equiptCalibratedInYear = new List<WorkOrderDetails>();

            foreach (var wo in woInYear)
            {
                equiptCalibratedInYear.AddRange(wo.WorkOrderDetails.ToList());
            }

            IList<WorkOrderDetails> equipCalibratedByVendor = equiptCalibratedInYear
                                                                    .Where(x => x.ServicePlace == 3).ToList();

            IList<CertificateStandard> standardInYear = m_CertificateStandardRespository.Get(filterStandard).ToList();

            IList<AE> aeInYear = m_AERespository.Get(filterAE).ToList();

            double totalPoValue = 0;
            double totalInvoiceValue = 0;

            var oldReport = m_ReportItemRespository.DbSet().AsExpandable().Where(x => x.Year == year).FirstOrDefault();
            JObject reportItem = null;

            if (oldReport != null)
            {
                reportItem = JObject.Parse(oldReport.Data);
            }

            if (quotesInYear.Count > 0)
            {
                for (int i = 1; i <= 12; i++)
                {
                    var firstDayOfMonth = GetFirstDayOfMonth(i, year);
                    var endDayOfMonth = GetLastDayOfMonth(i, year);

                    var noOfQuote =
                        quotesInYear.Count(x => x.QuoteDate >= firstDayOfMonth
                            && x.QuoteDate <= endDayOfMonth);

                    var poInMonth = poInYear.Where(x => x.PODate >= firstDayOfMonth
                            && x.PODate <= endDayOfMonth).ToList();

                    var invoiceInMonth = invoiceInYear.Where(x => x.InvoiceDate >= firstDayOfMonth
                            && x.InvoiceDate <= endDayOfMonth).ToList();

                    var noOfPo = poInMonth.Count;

                    var woInMonth = woInYear.Where(x => x.CalibrationDate >= firstDayOfMonth
                            && x.CalibrationDate <= endDayOfMonth).ToList();

                    var noOfWo = woInMonth.Count();

                    var noOfCert = certInYear.Count(x => x.CalibrationDate >= firstDayOfMonth
                            && x.CalibrationDate <= endDayOfMonth);


                    var noOfEquiqt = (from dt in equiptCalibratedInYear
                                      join wo in woInMonth on dt.WorkOrderId equals wo.WorkOrderId
                                      select dt.Quantity).Sum();

                    var noOfCustomer = customerInYear.Count(x => x.CreatedDate >= firstDayOfMonth
                            && x.CreatedDate <= endDayOfMonth);

                    var noOfEquipByVendor = (from dt in equipCalibratedByVendor
                                             join wo in woInMonth on dt.WorkOrderId equals wo.WorkOrderId
                                             select dt.Quantity).Count();

                    var noOfStandard = standardInYear.Count(x => x.CreatedDate >= firstDayOfMonth
                                                                 && x.CreatedDate <= endDayOfMonth);


                    var ae = aeInYear.Where(x => x.AEDate >= firstDayOfMonth
                                                                && x.AEDate <= endDayOfMonth);

                    var totalAE = ae.Sum(x => x.Total);

                    double total = 0;

                    foreach (var po in poInMonth)
                    {
                        if (po.Status != 1 && po.Status != 3)
                        {
                            double result = 0;
                            foreach (var poItem in po.Items)
                            {
                                result += poItem.UnitPrice * poItem.Quantity;
                            }
                            result += po.ServiceCharges;
                            result += result * ((double)po.Vat / 100);
                            total += result;
                        }
                    }

                    totalPoValue += total;


                    double totalInvoice = 0;

                    foreach (var po in invoiceInMonth)
                    {
                        double result = 0;
                        foreach (var poItem in po.Items)
                        {
                            result += poItem.UnitPrice * poItem.Quantity;
                        }
                        result += po.ServiceCharges;
                        result += result * ((double)po.Vat / 100);
                        totalInvoice += result;
                    }

                    totalInvoiceValue += totalInvoice;

                    var taxiFee = ae.Where(x => x.Reason == Reason.Taxi.ToString()).Sum(y => y.Total);
                    var diemFee = ae.Where(x => x.Reason == Reason.Diem.ToString()).Sum(y => y.Total);
                    var officeFee = ae.Where(x => x.Reason == Reason.OfficeSupplies.ToString()).Sum(y => y.Total);
                    var vendorFee = ae.Where(x => x.Reason == Reason.PayVendor.ToString()).Sum(y => y.Total);
                    var standardFee = ae.Where(x => x.Reason == Reason.Standards.ToString()).Sum(y => y.Total);
                    var marketingFee = ae.Where(x => x.Reason == Reason.MaketingCost.ToString()).Sum(y => y.Total);
                    var rental = ae.Where(x => x.Reason == Reason.OfficeRental.ToString()).Sum(y => y.Total);
                    var utilities = ae.Where(x => x.Reason == Reason.OfficeUtilities.ToString()).Sum(y => y.Total);
                    var phone = ae.Where(x => x.Reason == Reason.Phone.ToString()).Sum(y => y.Total);
                    var others = ae.Where(x => x.Reason == Reason.Others.ToString()).Sum(y => y.Total);
                    var taxes = ae.Where(x => x.Reason == Reason.Taxes.ToString()).Sum(y => y.Total);
                    var maintenance = ae.Where(x => x.Reason == Reason.OfficeMaintenance.ToString()).Sum(y => y.Total);
                    var payInterest = ae.Where(x => x.Reason == Reason.PayInterest.ToString()).Sum(y => y.Total);

                    var report = new ReportModel()
                    {
                        NoOfQuote = noOfQuote,
                        NoOfPO = noOfPo,
                        NoOfWO = noOfWo,
                        NoOfCert = noOfCert,
                        NoOfEquipmentCalibrated = noOfEquiqt,
                        NoOfNewCustomer = noOfCustomer,
                        NoOfEquiptCalibratedByVendors = noOfEquipByVendor,
                        NoOfStandardsDues = noOfStandard,
                        TotalAEExpenses = totalAE,
                        TotalPOValue = total,
                        TotalInvoiceValue = totalInvoice,
                        Other1 = taxiFee,
                        Other2 = diemFee,
                        Other3 = marketingFee,
                        Other4 = vendorFee,
                        Other5 = officeFee,
                        Other6 = rental,
                        Other7 = utilities,
                        Other8 = maintenance,
                        Other9 = phone,
                        Other10 = payInterest,
                        Other11 = others,
                        Other12 = taxes,
                        Other13 = standardFee
                    };


                    if (reportItem != null)
                    {
                        int operationExpenses;
                        int.TryParse((string)reportItem["operationExpenses"][i - 1], out operationExpenses);
                        report.OperationExpenses = operationExpenses;

                        int tax;
                        int.TryParse((string)reportItem["tax"][i - 1], out tax);
                        report.Tax = tax;
                    }

                    reports.Add(report);
                }

                var yearReport = new ReportModel()
                {
                    NoOfQuote = quotesInYear.Count,
                    NoOfPO = poInYear.Count,
                    NoOfWO = woInYear.Count,
                    NoOfCert = certInYear.Count,
                    NoOfEquipmentCalibrated = equiptCalibratedInYear.Select(x => x.Quantity).Sum(),
                    NoOfNewCustomer = customerInYear.Count,
                    NoOfEquiptCalibratedByVendors = equipCalibratedByVendor.Select(x => x.Quantity).Sum(),
                    NoOfStandardsDues = standardInYear.Count,
                    TotalPOValue = totalPoValue,
                    TotalInvoiceValue = totalInvoiceValue,
                    TotalAEExpenses = aeInYear.Sum(x => x.Total),
                    Other1 = reports.Sum(x => x.Other1),
                    Other2 = reports.Sum(x => x.Other2),
                    Other3 = reports.Sum(x => x.Other3),
                    Other4 = reports.Sum(x => x.Other4),
                    Other5 = reports.Sum(x => x.Other5),
                    Other6 = reports.Sum(x => x.Other6),
                    Other7 = reports.Sum(x => x.Other7),
                    Other8 = reports.Sum(x => x.Other8),
                    Other9 = reports.Sum(x => x.Other9),
                    Other10 = reports.Sum(x => x.Other10),
                    Other11 = reports.Sum(x => x.Other11),
                    Other12 = reports.Sum(x => x.Other12),
                    Other13 = reports.Sum(x => x.Other13),
                    OperationExpenses = reports.Sum(x => x.OperationExpenses),
                    Tax = reports.Sum(x => x.Tax),
                    Year = year
                };
                reports.Add(yearReport);
            }
            return reports;
        }

        private DateTime GetFirstDayOfMonth(int iMonth, int year)
        {
            // set return value to the last day of the month
            // for any date passed in to the method

            // create a datetime variable set to the passed in date
            DateTime dtFrom = new DateTime(year, iMonth, 1, 0, 0, 0);

            // remove all of the days in the month
            // except the first day and set the
            // variable to hold that date
            dtFrom = dtFrom.AddDays(-(dtFrom.Day - 1));

            // return the first day of the month
            return dtFrom;
        }

        private DateTime GetLastDayOfMonth(int iMonth, int year)
        {

            // set return value to the last day of the month
            // for any date passed in to the method

            // create a datetime variable set to the passed in date
            DateTime dtTo = new DateTime(year, iMonth, 1, 23, 59, 59);

            // overshoot the date by a month
            dtTo = dtTo.AddMonths(1);

            // remove all of the days in the next month
            // to get bumped down to the last day of the
            // previous month
            dtTo = dtTo.AddDays(-(dtTo.Day));

            // return the last day of the month
            return dtTo;

        }
    }
}
